package com.hmdp.utils;

import cn.hutool.core.lang.UUID;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Collections;
import java.util.concurrent.TimeUnit;

public class SimpleRedisLock implements ILock{
    private String name;    // 用户的 ID
    private StringRedisTemplate stringRedisTemplate;

    //  这个 是 setnx 的 key 的 组成 部分

    // 分布式 锁的 防止 误删，那么 就 需要

    private static final String ID_PREFIX = UUID.randomUUID().toString(true)+"-";

    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;

    static{  // 初始 化静态成员，  这样 只会 加载 一次，不用每次都 IO
        UNLOCK_SCRIPT = new DefaultRedisScript<>();
        UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));  // 这个 就是通过 lua 的位置来加载lua
        UNLOCK_SCRIPT.setResultType(Long.class);
    }

    //
    private static final String KEY_PREFIX="lock:";

    public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
        this.name = name;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean tryLock(long timeoutSec) {

        long id = Thread.currentThread().getId();  // 获取 当前线程的Id 同一个 JVM 里面 是 不同二点

        String threadid = ID_PREFIX + id;

        // 这条命令 就类似于  set key value ex time nx ;  这个reids 命令，单条 是 原子的，所以 同时 成功 失败， 这样就可以保证 设置值 和 设置过期时间 同时生效

        // 只取获取 锁 一次 失败 就 直接返回
        Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadid, timeoutSec, TimeUnit.SECONDS);  //


        return Boolean.TRUE.equals(aBoolean);  // 这样 返回的 就是 boolean 了， 减少 自动拆箱 和 装箱
    }

    @Override
    public void unLock() {
//        // 删除锁的时候 要判断是不是自己的锁,构建 自己的 ID
//        String mythreadid = ID_PREFIX+Thread.currentThread().getId();
//        //
//
//        // 然后 去 查一下 当前 的 锁 是被 谁 持有的
//        String threadid = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
//
//        // 现在的 问题 是，  当 删除锁 执行到 判断 是 自己的 锁 通过 后 阻塞，超时 释放，任然 会 导致 误删的问题
//        // 这是 因为 一个 原子操作 没有 按照 预期 进行
//
//        if(threadid.equals(mythreadid)) {  // 当判断 自己 持有 锁的 时候 就 可以删除了
//            // 删除 和 设置 有效期 是 通用 命令  对于 通用 命令 可以 直接 使用stringRedisTemplate 来 调用
//            stringRedisTemplate.delete(KEY_PREFIX + name);  // 删除其他的线程 就可以 加入了

        Long execute = stringRedisTemplate.execute(UNLOCK_SCRIPT, Collections.singletonList(KEY_PREFIX + name), ID_PREFIX + Thread.currentThread().getId());// 需要 一个 lua 脚本 和 传递 过去的 参数 key 和 value


        // 不是 多线程 和 异步 编排，而是
    }
}
